# an algorithm for playing the hunger games
# designed by Aaron Burke and Victor Phan 2013
# this is the final algorithm as to submission on brilliant.org
# the main feature of this algorithm is to make use of the randomly sorted players
# sorting out player_reputations and distributing decisions to that sorted
# this means that the decisions are randomly distributed depending on the randomness of brilliant.org

import os
import math
import random

def mean(lits): #Returns arithmetic mean of a list
	retr = 0
	for i in lits:
		retr += i/len(lits)
	return retr

class Player:
	def __init__(self):
		self.delta = 0.1
		# these are constants for determining the decision based on analysis of distribution of player_reputations
		
		self.huntP = 0.5               
		self.betrayP = 0.9
		self.emerP = 0.4
		
		self.players_start = 1 #To store number of players at start of game
		self.l_players = 0 #Number of players in the previous round
		self.food_trace = [] #Record the food over the last 10 turns
		self.rep_trace = [] #Record the reputations over the last 10 turns
		self.round_no = 1 #Round number
		self.hunts = 0 #Number of hunts mark1 (and, by extension, all other remaining players)
		self.s_hunts = 0 #Number of successsful hunts - ie hunts in which both mark1 and the other player hunted
		self.r_hunters = 0.0 #Number of hunters helping mark1 in a particular round
		self.mean_hunters = 0.0 #Ratio of hunters helping MAIN
		self.points = 0 #Points system to know when to sacrifice reputation for extra food
		self.th_hunt = 0.7 #Reputation threshold before mark1 will cooperate with another player
		self.th_betray = 0.80 #Threshold for own reputation before mark1 will betray another player
		self.th_emer = 0.6 #Emergency threshold - if reached, mark1 will hunt lots to make up lost reputation

	def hunt_choices(self, round_number, current_food, current_reputation, m, player_reputations):
		#print "___________________________________________________PLAYER_MAIN:", current_food, "_______REPUTATION:", current_reputation
		#print "REPUTATION", current_reputation
		#print "FOOD", current_food
		self.round_no = round_number
		retr = []
		
		# Gambits for opening rounds
		# The opening round is very important, after this, all players might portray a similar behaviour to us
		
		if round_number == 1:
			self.players_start = len(player_reputations) #Record initial number of players
			self.l_players = self.players_start
		if self.hunts <= 30:
			return ['h' for i in player_reputations] #First 15 hunts will be spent building a solid reputation
		elif self.hunts <= 50:
			for i in player_reputations:
				if random.random() < i:
					retr = retr + ['h']
				else:
					retr = retr + ['s']
			return retr #Not enough info in first 50 hunts to trust reputations, so use only basic algorithm that will partly protect mark1 and build his reputation
		
		#This is the start of the proper play

		#if self.l_players != len(player_reputations): #Adjust average for player deaths - MAYBE REVISIT??
		self.mean_hunters = self.mean_hunters/self.l_players*len(player_reputations)
		
		## Victor's edited code, taking the distribution of the players range, we can set the th_hunt and th_betray variables dynamically
		# This marks the beginning of Victor's code
		ordPlayRep = []
		ordPlayRep = list(player_reputations) 
		ordPlayRep.sort()
		
		self.th_hunt = mean([mean(player_reputations) * 2 * self.huntP,ordPlayRep[int(math.floor(len(player_reputations) * self.huntP))]])
		self.th_betray = mean([mean(player_reputations) * 2 * self.betrayP,ordPlayRep[int(math.floor(len(player_reputations) * self.betrayP))]])
		self.th_emer = mean([mean(player_reputations) * 2 * self.emerP,ordPlayRep[int(math.floor(len(player_reputations) * self.emerP))]])
	
		ticket = max(int(math.ceil(self.delta * len(player_reputations))), 1)
		
		for rep in ordPlayRep: 	# implementation of chaos and unpredictability of assignment of decisions to players
								# this is really just taking advantage of the system's randomiser
			if current_reputation <= self.th_emer:
				if rep >= ordPlayRep[ticket]:
					retr += 'h'
					#print "E H", current_reputation, rep, self.th_emer, ordPlayRep[ticket]
				else:
					retr += 's'
					#print "E S", current_reputation, rep, self.th_emer, ordPlayRep[ticket]
			else:
				if current_reputation <= self.th_betray:
					if rep >= self.th_hunt:
						retr += 'h'
						#print "N H", current_reputation, rep, self.th_hunt
					else:
						retr += 's'
						#print "N S", current_reputation, rep, self.th_hunt
				else:
					if rep > ordPlayRep[-ticket]:
						retr += 'h'
						#print "B H", current_reputation, rep, self.th_betray, ordPlayRep[-ticket]
					elif self.points < 0:
						retr += 's'
						#print "B S", current_reputation, rep, self.th_betray, ordPlayRep[-ticket]
					else:
						retr += 'h'
		return retr

		# This marks the end of Victor's code
		
	def hunt_outcomes(self, food_earnings):
		if food_earnings == 1: #Made up extra food
			if self.points < 0:
				self.points += 3 #Successful betrayal
				#print "SUCCESSFUL BETRAYAL"
			else:
				self.points += 1 #Lucky betrayal
				#print "LUCKY BETRAYAL"
			self.r_hunters += 1
		elif food_earnings == -3:
			self.points -= 3 #Was betrayed - need to make up food
			#print "GOT BETRAYED"
		elif food_earnings == 0:
			self.s_hunts += 1 #Record successful hunt
			self.r_hunters += 1
			#print "SUCCESSFUL HUNT"

	def round_end(self, award, m, number_hunters):
		self.mean_hunters +=  self.r_hunters/self.round_no - self.mean_hunters/self.round_no #Update ratio of hunters
		#print "MEAN_HUNTERS", self.mean_hunters
		self.r_hunters = 0
		self.hunts += 1